/**
 * @SqliteService 代表该对象启用了事物管理，但是只管理拥有@TX的方法
 * 
 * @TX修饰的方法运行时如果抛出异常，则所有数据库操作回滚！
 * 
 * 多事物冲突时，会自动排序竞争完成，类似于多线程的自旋竞争。 
 * 
 * */

//等待
const wait = time => {
	return new Promise((resolve,reject) => {
		setTimeout(resolve,time);
	})
}

//@SqliteService
class TestTransaction {

	//@Autowired
	school;

	//@Autowired
	classes;

	//@Autowired
	student;

	log;

	/*****************  测试回滚 **********************/

	//@TX
	async testInsertStudentThrowException(length=10){

		for(let i=0;i<length;i++){
			
			await this.school.save({name:`testSchool${i}`,address:'xx'})
			if(i === 9)
				throw `by by`			
		}

	}

	//测试插入学生
	async testInsertStudentRollback(){

		try{
			await this.testInsertStudentThrowException();
		}catch(e){
			this.log.info(`捕获异常：${e},查询是否导入了学校信息`)
			const schoolCount = await this.school.count({name_fuzzy:'testSchool'});
			if(schoolCount === 0)
				this.log.info(`没有发现学校插入数据，回滚成功`)
		}

	}



	/*****************  测试事物传播 **********************/
	 

	//@TX
	async testTransactionPropogation(){

		await this.school.save({name:`propogation`,address:'xx'})

		/**
		 * 调用该方法后 事物沿用父级的！ 操作完成后，依旧会抛出异常，回滚所有操作。
		 * 如果不想回滚，就必须try catch下面这行代码抛出的异常！
		 * 
		 * 注意：1. 事物传递需要在方法末尾显示的传入 '$TX' 参数，表示沿用事物。
		 * 		这主要是Nodejs单线的问题，无法像java一样，把事物数据绑定到线程上。
		 * 
		 *		2. 还有这种调用情况  事物方法A -> 普通方法 -> 事物方法B，这里就没办法解决了，除非在方法间链式传递$TX参数，但是太麻烦了。
		 * 
		 * 建议: 1.最好只在重要的方法上面加事物，
		 * 		 2.开发时尽量事物调用事物。
		 * 
		 * */
		await this.testInsertStudentThrowException(10,'$TX')
	}


	/******************** 事物隔离  *************************/

	//@TX
	async testTransaction1(){
		this.log.method('testTransaction1').info('testTransaction1 运行了')
		//1秒和3秒后插入一条数据
		await wait(1000)
		await this.school.save({name:`SchoolIsolate1`,address:'xx'})
		await wait(2000)
		await this.school.save({name:`SchoolIsolate3`,address:'xx'})
	}

	//@TX
	async testTransaction2(){
		this.log.method('testTransaction2').info('testTransaction2 运行了')
		await this.school.save({name:`SchoolIsolate2`,address:'xx'})
		//2秒后抛出异常 如果隔离不成功的话 SchoolIsolate1，SchoolIsolate2 将会回滚！ 
		await wait(2000)
		throw 'byby'
	}


	async testTransactionIsolate(){

		//异步运行
		this.testTransaction1();

		try{
			/**
			 * 这里需要捕获异常 所以需要await 
			 * 表面上看testTransaction1和testTransaction2并发运行
			 * 实际testTransaction2只会在testTransaction1事物提交后运行。 --!
			 * 
			 * */
			 await this.testTransaction2();
		}catch(e){
			this.log.error(`testTransaction2 抛出异常:${e}`)
		}

		await wait(5000);

		this.log.info(`两个事物全部运行结束`)

		const isolateCount = await this.school.count({name_fuzzy:'SchoolIsolate'})

		if(isolateCount === 2){
			this.log.info('SchoolIsolate1和3插入成功，表示testTransaction1和testTransaction2事物隔离成功!')
		}

	}


	async doTest(){


		/*****************  测试回滚 **********************/


		await this.testInsertStudentRollback();



		/*****************  测试事物传播 **********************/

		/**
		 * 因为NODE单线程 和 SQLITE本身事物模型的限制
		 * 只开发了一种传播特性 REQUIRED ，基本上平时开发也就使用这一种 。
		 * （如果想用子事物的话，可以把数据放入回调处理）
		 * */

		 try{
 			await this.testTransactionPropogation();
		 }catch(e){

		 }

		 /******************** 事物隔离  *************************/

		 /**
		  * 这里的事物隔离类似于多线程的自旋竞争
		  * 同一时间只会开辟运行一个事物，其它的事物只能等待。
		  * [trace]SpringSqlite3TranscationProxy=>waitAllowOpen: tx:3049 => 开启事物
		  *	[trace]SpringSqlite3TranscationProxy=>waitAllowOpen: tx:8705 => 等待事务开启
		  * 当然性能上肯定拉跨，但是对于99%的网站来说 足够了。
		  * 
		  * */
		 await this.testTransactionIsolate();

	}


}

module.exports = {TestTransaction}